/**
 * gislin 2017年7月17日
 */
package com.btcode.orm.model;

import com.btcode.db.conver.DefaultValueConver;
import com.btcode.db.conver.IValueConver;
import com.btcode.db.sqlparameter.SQLParameterList;
import com.btcode.exception.MsgException;
import com.btcode.orm.relation.ex.IRelation;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 设计的思考，针对List<IField> fields有三种设计： 1、List<IField> fields放在BaseModel基类中，并定义为静态 好处：
 * a.可以节省内存，不用每次实例化model初始化fields， b.在基类中可以定义很多针对List<IField> fields的公共操作，这样子类就可以很干净 坏处：
 * a.继承BaseModel的model将无法修改List<IField> fields，因为List<IField> fields是静态，而且属于BaseModel
 * 这一条坏处，就把上面的好处全部抵消了
 * <p>
 * 2、List<IField> fields放在子类中，并定义为静态 好处： 解决上面提到的坏处 坏处： 针对List<IField>
 * fields的公共操作，就只能放在子类里面了，这样子类会很繁琐
 * <p>
 * 3、List<IField> fields放在BaseModel基类中，并定义为非静态 好处： 解决上面提到的坏处 坏处： a.由于是非静态，在性能上不是最优
 * b.如果需要访问model的fields，只能实例化后再访问，不够优雅
 * <p>
 * 针对上面思索再三，从代码的可维护性，和开发的体验度来考虑，决定采用第三种方案，如果有更好的方案，再来修改
 *
 * @author gislin 2017年7月17日
 */

public abstract class BaseModel extends HashMap<String, Object> implements IModel {

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    private HashMap<String, List<? extends IModel>> privateRrelationModelMap = new HashMap<>();
    ;

    /**
     * 为了提升性能，每个model中定义的IField存储下来，避免每次初始化都反射一遍
     */
    private static HashMap<String, List<IField>> modelFieldMapping = new HashMap<>();
    /**
     * 为了提升性能，把每个model的addition缓存起来
     */
    @SuppressWarnings({"rawtypes"})
    private static HashMap<String, IModelAddition> modelAdditionMapping = new HashMap<>();

    private static HashMap<String, List<IDomainField>> modelDomainFieldMapping = new HashMap<>();

    /**
     * 字段集合
     */
    protected List<IField> fields = new ArrayList<>();
    /**
     * 域集合
     */
    protected List<IDomainField> domainFields = new ArrayList<>();
    /**
     * 关系集合
     */
    protected List<IRelation> relations = new ArrayList<>();
    /**
     * 值转换器
     */
    IValueConver valueConver = new DefaultValueConver();

    public BaseModel() {
        executeModelFieldInfo();
        executeModelAddition();
        put("relationModelMap", privateRrelationModelMap);
    }

    /**
     * 初始化model的字段信息 把子类model的IField属性都解析出来，放入字段集合中 gislin 2017年9月19日
     */
    private void executeModelFieldInfo() {
        try {
            List<IField> modelFields = getModelFields();
            fields.clear();
            fields.addAll(modelFields);
            List<IDomainField> modelDomainFields = getModelDomainFields();
            domainFields.clear();
            domainFields.addAll(modelDomainFields);

        }
        catch (Exception e) {
            throw new MsgException(this.getClass().toString() + "初始化出错", e);
        }
    }

    /**
     * 获取model里的IField gislin 2017年9月19日
     */
    private List<IField> getModelFields() throws IllegalArgumentException, IllegalAccessException {
        String modelName = this.getClass().getName();
        if (!modelFieldMapping.containsKey(modelName)) {
            synchronized (modelFieldMapping) {
                if (!modelFieldMapping.containsKey(modelName)) {
                    generaterModelFields(modelName);
                }
            }
        }
        return modelFieldMapping.get(modelName);

    }

    private List<IDomainField> getModelDomainFields()
        throws IllegalArgumentException, IllegalAccessException {
        String modelName = this.getClass().getName();
        if (!modelDomainFieldMapping.containsKey(modelName)) {
            synchronized (modelDomainFieldMapping) {
                if (!modelDomainFieldMapping.containsKey(modelName)) {
                    generaterModelDomainFields(modelName);
                }
            }
        }
        return modelDomainFieldMapping.get(modelName);
    }

    /**
     * 反射获取field信息
     */
    private void generaterModelFields(String modelName)
        throws IllegalArgumentException, IllegalAccessException {
        List<IField> modelFields = new ArrayList<>();
        List<IDomainField> modelDomainFields = new ArrayList<>();
        Field[] fieldArray = this.getClass().getFields();
        for (Field field : fieldArray) {
            if (field.getType() == IField.class) {
                field.setAccessible(true);
                IField modelField = (IField) field.get(null);
                modelFields.add(modelField);
            }
            else if (field.getType() == IDomainField.class) {
                field.setAccessible(true);
                IDomainField modelField = (IDomainField) field.get(null);
                modelDomainFields.add(modelField);
            }
        }
        modelFieldMapping.put(modelName, modelFields);
        modelDomainFieldMapping.put(modelName, modelDomainFields);
    }

    /**
     * 反射获取field域信息
     */
    private void generaterModelDomainFields(String modelName)
        throws IllegalArgumentException, IllegalAccessException {
        List<IDomainField> modelDomainFields = new ArrayList<>();
        Field[] fieldArray = this.getClass().getFields();
        for (Field field : fieldArray) {
            if (field.getType() == IDomainField.class) {
                field.setAccessible(true);
                IDomainField modelField = (IDomainField) field.get(null);
                modelDomainFields.add(modelField);
            }
        }
        modelDomainFieldMapping.put(modelName, modelDomainFields);
    }

    /**
     * 处理附加的model信息 gislin 2017年9月19日
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private void executeModelAddition() {
        IModelAddition modelAddition = getModelAddition();
        if (modelAddition != null) {
            modelAddition.initField(this.fields);
            modelAddition.initRelations(this.relations);
            modelAddition.initDomainField(this.domainFields);
        }
    }

    @Override
    public Object getFieldValue(String columnName) {
        Object value = get(columnName);
        return value;
    }

    @Override
    public void setFieldValue(String columnName, Object value) {
        put(columnName, value);
    }

    @Override
    public <Model extends IModel>List<Model>  getRelationModel(String tableName) {
        if (privateRrelationModelMap == null) {
            return null;
        }
        List<? extends IModel> relationModels = privateRrelationModelMap.get(tableName);
        return (List<Model>) relationModels;
    }

    @Override
    public List<IField> getKeyFields() {
        List<IField> fields = getFields();
        List<IField> keyFields = new ArrayList<>();
        for (IField field : fields) {
            if (field.isKey()) {
                keyFields.add(field);
            }
        }
        return keyFields;
    }

    @Override
    public <Model extends IModel> void setRelationModel(String relationTableName, List<Model> models) {
        if (privateRrelationModelMap == null) {
            privateRrelationModelMap = new HashMap<>();
        }
        privateRrelationModelMap.put(relationTableName, models);
    }

    @Override
    public <Model extends IModel>void setRelationModels(HashMap<String, List<Model>> relationModels){
        if (privateRrelationModelMap == null) {
            privateRrelationModelMap = new HashMap<>();
        }
        privateRrelationModelMap.putAll(relationModels);
    }

    /**
     * 添加字段 gislin 2017年8月24日
     */
    protected void addField(IField field) {
        fields.add(field);
    }

    /**
     * 添加子表关联关系 gislin 2017年8月24日
     */
    protected void addRelation(IRelation realtion) {
        relations.add(realtion);
    }

    /**
     * 增加域字段 gislin 2017年8月24日
     */
    protected void addDomainField(IDomainField field) {
        domainFields.add(field);
    }

    @Override
    public List<IRelation> getRelations() {
        return relations;
    }

    @Override
    public List<IField> getFields() {
        return fields;
    }

    public List<IField> getUniques() {
        List<IField> uniqueFields = new ArrayList<>();
        List<IField> fields = getFields();
        for (IField field : fields) {
            if (field.isUnique()) {
                uniqueFields.add(field);
            }
        }
        return uniqueFields;
    }

    @Override
    public List<IDomainField> getDomainFields() {
        return domainFields;
    }

    public IValueConver getValueConver() {
        return valueConver;
    }

    @Override
    @SuppressWarnings({"rawtypes", "unchecked"})
    public IModelAddition getModelAddition() {
        String modelName = this.getClass().getName();
        if (!modelAdditionMapping.containsKey(modelName)) {
            synchronized (modelAdditionMapping) {
                if (!modelAdditionMapping.containsKey(modelName)) {
                    generaterModelAddition(modelName);
                }
            }
        }
        return modelAdditionMapping.get(modelName);
    }

    @SuppressWarnings("rawtypes")
    private void generaterModelAddition(String modelName) {
        try {
            Class<?> additionClass = Class.forName(modelName + "Addition");
            IModelAddition modelAddition = (IModelAddition) additionClass.newInstance();
            modelAdditionMapping.put(modelName, modelAddition);
        }
        catch (Exception e) {
            /**
             * 如果有异常，就不处理addition的逻辑
             */
        }
    }

    @Override
    public String toString() {
        Set<Entry<String, Object>> entrys = entrySet();
        StringBuffer strBuf = new StringBuffer("\r\n【model】" + getTableName() + ":\r\n");
        for (Entry<String, Object> entry : entrys) {
            strBuf.append("[" + entry.getKey() + "]:" + entry.getValue() + ", ");
        }
        List<IRelation> relations = getRelations();
        for (IRelation relation : relations) {
            List<IModel> models;
            String tableName = null;
            if (relation.isManyToMany()) {
                tableName = relation.getM2mMasterField().getTableName();
            }
            else {
                tableName = relation.getSlaveField().getTableName();
            }
            models = getRelationModel(tableName);
            strBuf.append("\r\n【Relation】:" + tableName + ":\r\n");
            strBuf.append(models);
        }
        return strBuf.toString();
    }

    public SQLParameterList toSQLParameter(Integer index) {
        List<IField> fields = getFields();
        SQLParameterList parameters = new SQLParameterList();
        com.btcode.orm.conver.IValueConver vc = new com.btcode.orm.conver.DefaultValueConver();
        String indexStr = index == null ? "" : index.toString();
        for (int i = 0; i < fields.size(); i++) {
            IField field = fields.get(i);
            Object value = getFieldValue(field.getFieldName());
            if (value == null) {
                continue;
            }
            else if (value.equals(FieldValue.nullValue)) {
                parameters.put(field.getFieldName() + indexStr, null);
            }
            else {
                value = vc.conveToSQLValue(field, value);
                parameters.put(field.getFieldName() + indexStr, value);
            }
        }
        return parameters;
    }

    public boolean isFromCache() {
        Object value = getFieldValue("dataFrom");
        if (value == null) {
            return false;
        }
        else if (value.toString().toLowerCase().equals("cache")) {
            return true;
        }
        return false;
    }
}
